package com.bluebench.bigul.domain;

import java.lang.reflect.Constructor;
import java.lang.reflect.InvocationTargetException;
import java.util.List;

import com.bluebench.bigul.domain.Element.Parameter;
import com.bluebench.bigul.exception.GraphicInitializationException;
import com.bluebench.bigul.service.TypeFactory;
import com.bluebench.bigul.service.processor.GenericProcessor;

public class ConstructorInvocation extends Invocation{

	public ConstructorInvocation() {
		setElementType(ElementType.INVOCATION);
	}
	
	@Override
	public Object invoke() throws SecurityException, IllegalArgumentException, ClassNotFoundException, NoSuchMethodException, IllegalAccessException, InvocationTargetException, InstantiationException, NoSuchFieldException, GraphicInitializationException {
		String classname = getClassName();
		Class<?> classtype = Class.forName(classname);
		List<Parameter> parameters = getParameters();
		Class<?>[] constructorArgClassTypes = getParameterTypes(parameters);
		Object[] constructorArgValues = new Object[parameters.size()];
		Object returnObj=null;
		if(parameters!=null && (!parameters.isEmpty())){
			for(int i=0;i<parameters.size();i++){
				Parameter p = parameters.get(i);
				p.setProcessor(GenericProcessor.getInstance(GenericProcessor.PARAMETER_PROCESSOR));
				Object iTextParamObj = p.process();
				constructorArgValues[i] = iTextParamObj;
			}			
			returnObj = instantiate(classtype, constructorArgClassTypes, constructorArgValues);
			
		}else{
			returnObj = Class.forName(classname).newInstance();
		}
		return returnObj;
	}	
	
	private Object instantiate(Class<?> className,Class<?>[] constructorArgClassTypes,Object[] constructorArgValues) throws IllegalArgumentException, InstantiationException, IllegalAccessException, InvocationTargetException, SecurityException,  ClassNotFoundException{
		Object instance = null;
		try{
			Constructor<?> constructor = className.getConstructor(constructorArgClassTypes);				
			/*for(Object arg : constructorArgValues){
				Class<?> c = arg.getClass();			
			}*/				
			instance = constructor.newInstance(constructorArgValues);
		
		}catch(NoSuchMethodException e){
			
			Constructor<?>[] ctors = className.getConstructors();
			boolean flag=false;
			for(Constructor<?> c : ctors){
				Class<?>[] types = c.getParameterTypes();
				if(types.length == constructorArgClassTypes.length){
					for(int i=0;i<types.length;i++){
						if(types[i].isAssignableFrom(constructorArgClassTypes[i])){
							flag=true;
							constructorArgValues[i] = types[i].cast(constructorArgValues[i]);
						}else{
							flag=false;
						}
					}
					if(flag){
						instance = c.newInstance(constructorArgValues);
					}
				}
			}		
		}
		
		return instance;
	}
	
	
	
	
	private Class<?>[] getParameterTypes(List<Parameter> parameters) throws ClassNotFoundException{
		
		Class<?>[] paramclassArray = new Class<?>[parameters.size()]; 
		
		for(int i=0;i<parameters.size();i++){
			String type = parameters.get(i).getType();
			Types t=null;
			String className=null;
			if(type==null){
				t = Types.getType("default");
				className=t.getClassName();
				paramclassArray[i]=Class.forName(className);
			}else{
				if(type.startsWith("_")){
					paramclassArray[i]=TypeFactory.getPrimitiveType(type.replace("_","").trim());
				}else{
					t = Types.getType(type);
					className=t.getClassName();
					paramclassArray[i]=Class.forName(className);
				}
				
			}			
			
		}
		
		return paramclassArray;		
	}	

}
